#pragma once

#define __HUGE

#include "v8/v8.h"
#include "v8/cproxyv8-class.h"

#include <gtl/io/ostrm.h>
#include <gtl/string/str.h>
#include <gtl/script/script.h>

//////////////////////////////////////////////////////////////////////////
// v8
// http://v8.googlecode.com/svn/trunk
// v8 version: 2.2.8, svn version: 4588
//
// cproxy v8
// http://cproxyv8.googlecode.com/svn/trunk
//////////////////////////////////////////////////////////////////////////

#ifdef _DEBUG
//#pragma comment(lib, "v8d.lib")
#pragma comment(lib, "preparser_libD.lib")
#pragma comment(lib, "v8_nosnapshotD.lib")
#pragma comment(lib, "v8_snapshotD.lib")
#pragma comment(lib, "v8_baseD.lib")
#else
//#pragma comment(lib, "v8.lib")
#pragma comment(lib, "preparser_lib.lib")
#pragma comment(lib, "v8_nosnapshot.lib")
#pragma comment(lib, "v8_snapshot.lib")
#pragma comment(lib, "v8_base.lib")
#endif

#pragma comment(lib, "winmm.lib")
#pragma comment(lib, "Ws2_32.lib")

namespace gtl
{

class jscript
	: public gtl::script
{
public:
	jscript()
	{
	}

	virtual ~jscript()
	{
		if(m_context.IsEmpty())
			m_context->Exit();

		//v8::V8::Dispose();
	}

public:
	virtual void init()
	{
		v8::HandleScope handle_scope;

		m_global = v8::ObjectTemplate::New();
		m_global->Set(v8::String::New("print"), v8::FunctionTemplate::New(jscript::print));

		m_context = v8::Context::New(NULL, m_global);
		m_context->Enter();
	}

public:
	virtual bool init(const gtl::str& src, gtl::str* result = NULL)
	{
		return eval(src, result);
	}

	virtual bool eval(const gtl::str& src, gtl::str* result = NULL)
	{
		if(src.empty())
			return false;

		if(m_global.IsEmpty())
		{
			gtl::dout << "uninitialized\n";
			return false;
		}

		m_result.clear();
		m_exception.clear();

		v8::TryCatch try_catch;
		v8::HandleScope handle_scope;
		v8::Handle<v8::Script> script = v8::Script::Compile(v8::String::New(src.utf8().c_str()));
		if(script.IsEmpty())
		{
			// Print errors that happened during compilation.
			exception(&try_catch);
			return false;
		}

		v8::Handle<v8::Value> ret = script->Run();
		if(ret.IsEmpty() && try_catch.HasCaught())
		{
			// Print errors that happened during execution.
			exception(&try_catch);
			return false;
		}

		if(!ret->IsUndefined() && !ret.IsEmpty())
		{
			// If all went well and the result wasn't undefined then print
			// the returned value.
			v8::String::Utf8Value str(ret);
			m_result = cstr(str);
			if(result != NULL)
				*result = m_result;

			if(!m_result.empty())
				gtl::rout << m_result << "\n";
		}

		return true;
	}

	virtual bool call(const gtl::str& fun, gtl::str* result = NULL)
	{
		return eval(fun, result);
	}

public:
	static gtl::str cstr(const v8::String::Utf8Value& value)
	{
		return gtl::str(*value ? *value : "").trans(CP_UTF8).trans();
	}

	static gtl::str cstr(const v8::Arguments& args, int index)
	{
		return cstr(v8::String::Utf8Value(args[index]));
	}

protected:
	void exception(v8::TryCatch* try_catch)
	{
		v8::HandleScope handle_scope;

		v8::String::Utf8Value exception(try_catch->Exception());
		gtl::str exception_string = cstr(exception);
		v8::Handle<v8::Message> message = try_catch->Message();
		if (message.IsEmpty())
		{
			// V8 didn't provide any extra information about this error; just
			// print the exception.
			m_exception = exception_string;
		}
		else
		{
			// Print (filename):(line number): (message).
			v8::String::Utf8Value filename(message->GetScriptResourceName());
			gtl::str filename_string = cstr(filename);
			int linenum = message->GetLineNumber();
			m_exception << filename_string << ":" << linenum << ": " << exception_string << "\n";

			// Print line of source code.
			v8::String::Utf8Value sourceline(message->GetSourceLine());
			gtl::str sourceline_string = cstr(sourceline);
			m_exception << sourceline_string << "\n";

			// Print wavy underline (GetUnderline is deprecated).
			int start = message->GetStartColumn();
			for (int i = 0; i < start; i++)
			{
				m_exception << " ";
			}

			int end = message->GetEndColumn();
			for (int i = start; i < end; i++)
			{
				m_exception << "^";
			}

			m_exception << "\n";
		}

		gtl::rout << m_exception;
	}

	static v8::Handle<v8::Value> print(const v8::Arguments& args)
	{
		bool first = true;
		for (int i = 0; i < args.Length(); i++)
		{
			v8::HandleScope handle_scope;
			if (first)
				first = false;
			else
				gtl::rout << " ";                                                   

			v8::String::Utf8Value str(args[i]);
			gtl::rout << cstr(str);
		}

		return v8::Undefined();
	}

protected:
	v8::Handle<v8::ObjectTemplate> m_global;
	v8::Handle<v8::Context> m_context;

	gtl::str m_result;
	gtl::str m_exception;
};

} // end of namespace gtl
